package danran.dbapi.core.engine;


import danran.dbapi.core.SqlMeta;
import danran.dbapi.core.context.Context;
import danran.dbapi.core.log.Logger;
import danran.dbapi.core.node.SqlNode;
import danran.dbapi.core.handler.XmlParser;
import danran.dbapi.core.token.TokenHandler;
import danran.dbapi.core.token.TokenParser;

import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

/**
 * 动态sql解析引擎
 *
 * @Classname DynamicSqlEngine
 * @Description TODO
 * @Date 2022/1/14 13:45
 * @Created by RanCoder
 */
public class DynamicSqlEngine {

    private static final Logger log = Logger.getInstance(DynamicSqlEngine.class);

    Cache cache = new Cache();

    public SqlMeta parse(String text, Map<String, Object> params) {
        // 添加root格式标识——>变为xml数据，便于后续的解析
        text = String.format("<root>%s</root>", text);
        // 解析xml格式化的 sql text
        SqlNode sqlNode = parseXml2SqlNode(text);
        // 创建context
        Context context = new Context(params);
        // 解析标签，去除标签，替换 ${}为常量值, #{}保留不变
        parseSqlText(sqlNode, context);
        // #{}替换成?，并且将?对应的参数值按顺序保存起来
        parseParameter(context);
        return new SqlMeta(context.getSql(), context.getJdbcParameters());
    }

    public Set<String> parseParameter(String text) {
        text = String.format("<root>%s</root>", text);
        SqlNode sqlNode = parseXml2SqlNode(text);
        HashSet<String> set = new HashSet<>();
        sqlNode.applyParameter(set);
        return set;
    }

    /**
     * 将xml配置转换为<code>SqlNode</code>
     *
     * @param text xml文本
     * @return <code>SqlNode</code>对象
     */
    private SqlNode parseXml2SqlNode(String text) {
        SqlNode node = cache.getNodeCache().get(text);
        if (node == null) {
            node = XmlParser.parseXml2SqlNode(text);
            cache.getNodeCache().put(text, node);
        }
        return node;
    }

    /**
     * 解析标签，去除标签，替换 ${}为常量值, #{}保留不变
     *
     * @param sqlNode Sql节点
     * @param context
     */
    private void parseSqlText(SqlNode sqlNode, Context context) {
        sqlNode.apply(context);
    }

    /**
     * #{}替换成?，并且将?对应的参数值按顺序保存起来
     *
     * @param context
     */
    private void parseParameter(Context context) {
        TokenParser tokenParser = new TokenParser("#{", "}", new TokenHandler() {
            @Override
            public String handleToken(String content) {
                // content为 `#{}` 包裹的字符串——参数名称， 根据该参数名称从context中得到对应的参数值
                Object value = context.getOgnlValue(content);
                if (value == null) {
                    throw new RuntimeException("could not found value : " + content);
                }
                // 按照参数的顺序，依次添加参数值
                context.addParameter(value);
                // 并把 `#{......}`替换为 ?
                return "?";
            }
        });
        String sql = tokenParser.parse(context.getSql());
        log.info("Parse sql : " + sql);
        context.setSql(sql);
    }

    public static void main(String[] args) {
        DynamicSqlEngine engine = new DynamicSqlEngine();
        String sql = ("<root>select <if test='minId != null'>id > ${minId} #{minId} <if test='maxId != null'> and id &lt; ${maxId} #{maxId}</if> </if></root>");
        Map<String, Object> map = new HashMap<>();
        map.put("minId", 100);
        map.put("maxId", 500);
        engine.parse(sql, map);
        map.clear();
        SqlMeta parse = engine.parse("<root>SELECT * FROM user;</root>", map);
    }
}
